import socket
import requests
import json
from utils import block_to_json
from block import Block
from block_chain import add_block, miner_ip, is_valid_chain

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.bind(("", 22321))

# 广播数据列表
broadcast_info = []

# 是否同步区块链，第一次进入网路时需要同步其他节点的区块链
is_async_block_chain = False


# 开始监听
def start_p2p():
    """
    p2p 监听函数
    :return:
    """

    print("开始监听")

    while 1:
        data, addr = s.recvfrom(1024)
        if not data:
            continue
        # 如果不是本机ip地址，则不接受广播
        if addr[0] == miner_ip:
            continue
        # 如果是第一次进入网络，同步区块链数据
        if not is_async_block_chain:
            print("正在同步区块链。。。请稍后")
            url = "http://" + addr[0] + ":5000/static/blocks.json"
            res = requests.get(url).json()
            async_chain(res)

        # 将接收到的数据转为 Block对象
        block = Block(**eval(data))
        # 添加到广播数据列表，方便前端展示
        broadcast_info.append(block.get_block_info())
        if block.miner_ip != miner_ip:
            print("<<<广播地址", addr)
            print("<<<接收到的广播数据", str(data))
            # 添加到区块链中
            add_block(block, add_cb)
    s.close()


def add_cb(block):
    """
    添加区块的回调函数
    :return: void
    """

    if block:
        print("添加区块成功")
    else:
        print("区块验证失败，拒绝添加")


def async_chain(block_json):
    """
    同步区块链
    :param block_json: 区块json数据
    :return: True or False
    """

    # 转为对象形式
    block_chain = list(map(lambda x: Block(**x), block_json))
    # 验证区块是否合法
    is_valid = is_valid_chain(block_chain)
    if not is_valid:
        print("区块不合法，拒绝同步")
        return False
    else:
        # 合法，写入blocks.json
        file = open('static/blocks.json', 'w', encoding='utf-8')
        file.write(json.dumps(block_json))
        # 设为已经同步
        global is_async_block_chain
        is_async_block_chain = True
